package personnal.blog2pdf.writer;

import java.awt.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;

import personnal.blog2pdf.data.PostContent;

import com.lowagie.text.BadElementException;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;

@Component
public class PostContentPdfWriter implements ItemWriter<PostContent>, ItemStream {

    private static final Logger logger = Logger.getLogger(PostContentPdfWriter.class);

    private static final int DEFAULT_MARGIN = 50;

    private static final String TITLE_RIGHT_CORNER = "title-right-corner.gif";

    private static final String TITLE_BACKGROUND = "title-background.gif";

    private static final String TITLE_LEFT_CORNER = "title-left-corner.gif";

    private static final Font POST_TITLE_FONT = new Font(Font.HELVETICA, 17, Font.BOLD, Color.WHITE);

    private final Map<String, Image> imageCache = new HashMap<String, Image>();

    protected Document document;
    protected PdfWriter pdfWriter;
    private DateTime currentMonth;

    private DateTimeFormatter dateParser;

    private static final String filePrefix = "babyblog-";
    private static final String targetDirectory = "target";

    @Override
    public void write(List<? extends PostContent> postContents) throws Exception {
        for (PostContent post : postContents) {
            if (this.currentMonth == null || post.getDate().getMonthOfYear() != this.currentMonth.getMonthOfYear()
                    || post.getDate().getYear() != this.currentMonth.getYear()) {
                this.currentMonth = post.getDate();
                initPdfFile();
            }
            try {
                createTitleBar(post.getTitle());
                convertHtmlToPdf(post.getContent());
            } catch (Exception e) {
                throw new WriteException("Can't write post content chunk", e);
            }
        }
    }

    private void createTitleBar(String title) throws DocumentException, IOException {
        Paragraph titleBar = new Paragraph();
        Image cornerImg = getImage(TITLE_LEFT_CORNER);
        titleBar.add(new Chunk(cornerImg, 0, 0));

        Image bgImg = getImage(TITLE_BACKGROUND);
        bgImg.scaleAbsoluteWidth(475);

        float width = bgImg.getPlainWidth();
        float height = bgImg.getPlainHeight();
        PdfTemplate tmp = createPdfTemplate(width, height);
        tmp.addImage(bgImg, width, 0, 0, height, 0, 0);
        BaseFont font = BaseFont.createFont(POST_TITLE_FONT.getFamilyname(), BaseFont.WINANSI, true);
        Validate.notNull(font, "Can't get base font from font");
        tmp.beginText();
        tmp.setGrayFill(1);
        tmp.setFontAndSize(font, POST_TITLE_FONT.getSize());
        tmp.showTextAligned(Element.ALIGN_CENTER, title, width / 2, 4, 0);
        tmp.endText();
        titleBar.add(new Chunk(Image.getInstance(tmp), 0, 0));
        Image flipedCornerImg = getImage(TITLE_RIGHT_CORNER);
        titleBar.add(new Chunk(flipedCornerImg, 0, 0));
        this.document.add(titleBar);
    }

    protected PdfTemplate createPdfTemplate(float width, float height) {
        return this.pdfWriter.getDirectContent().createTemplate(width, height);
    }

    private Image getImage(String imageFilename) throws BadElementException, IOException {
        if (this.imageCache.containsKey(imageFilename)) {
            return this.imageCache.get(imageFilename);
        }
        URL cornerImgUrl = getImageUrl(imageFilename);
        Image image = Image.getInstance(cornerImgUrl);
        this.imageCache.put(imageFilename, image);
        return image;
    }

    protected URL getImageUrl(String imageFilename) {
        URL cornerImgUrl = Thread.currentThread().getContextClassLoader().getResource(imageFilename);
        return cornerImgUrl;
    }

    protected void convertHtmlToPdf(String content) throws Exception {
        PdfRenderer pdfRenderer = createPdfRenderer();
        try {
            pdfRenderer.setDocumentFromString(content);
        } catch (Exception e) {
            throw new Exception("Impossible de parser le HTML", e);
        }
        pdfRenderer.layout();
        pdfRenderer.createPDF(this.document, this.pdfWriter);
        this.document.newPage();
    }

    protected PdfRenderer createPdfRenderer() {
        return new MyITextRenderer();
    }

    @Override
    public void open(ExecutionContext executionContext) throws ItemStreamException {
    }

    protected void initPdfFile() throws DocumentException, FileNotFoundException {
        if (this.document != null || this.pdfWriter != null) {
            close();
        }
        this.dateParser = DateTimeFormat.forPattern("yyyy-MM");
        String period = this.dateParser.print(this.currentMonth);
        File outputFile = new File(targetDirectory, filePrefix + period + ".pdf");
        logger.info("Creating pdf document for file: " + outputFile);
        this.document = new Document(PageSize.A4, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN);
        this.pdfWriter = PdfWriter.getInstance(this.document, new FileOutputStream(outputFile));
        logger.info("Opening pdf document");
        this.document.open();
    }

    @Override
    public void update(ExecutionContext executionContext) throws ItemStreamException {
    }

    @Override
    public void close() throws ItemStreamException {
        if (this.document != null) {
            this.document.close();
        }
        if (this.pdfWriter != null) {
            this.pdfWriter.close();
        }
    }

}
